﻿#region Includes
using System;
using OpenTK;
using OpenTK.Graphics.OpenGL;
#endregion

namespace GingerGL.Graphics
{
    //------------------------------------------------------------//
    /// <summary>
    /// First Person Camera
    /// </summary>
    public class FirstPersonCamera : Camera
    {
        //--------------------------------------------------------//
        #region Variables
        /// <summary>
        /// This is the default forward speed 
        /// </summary>
        public const float DefaultForwardSpeed = 20f / 60f;
        /// <summary>
        /// This is the default Rotaion Speed
        /// </summary>
        public const float DefaultRotationSpeed = 1f / 60f;
        #endregion
        //--------------------------------------------------------//
        #region Properties
        /// <summary>
        /// Gets/Sets Camera Movement Speed
        /// </summary>
        public float MoveSpeed { get; set; }
        /// <summary>
        /// Gets/Sets Camera Rotation speed
        /// </summary>
        public float RotationSpeed { get; set; }
        /// <summary>
        /// Gets Camera Tilt
        /// </summary>
        public float Pitch { get; protected set; }
        /// <summary>
        /// Gets Camera Spin
        /// </summary>
        public float Yaw { get; protected set; }
        /// <summary>
        /// Gets the Cameras Strafe Direction
        /// </summary>
        public Vector3 Strafe { get; protected set; }
        #endregion
        //--------------------------------------------------------//
        #region Constructors
        /// <summary>
        /// Initializes a new instance of object
        /// </summary>
        public FirstPersonCamera()
        {
            this.Yaw = 0.0f;
            this.Pitch = 0.0f;
            this.Position = new Vector3(0f, 0f, 0f);
            this.View = new Vector3(0f, 0f, 1f);
            this.UpVector = new Vector3(0f, 1f, 0f);
            this.Strafe = Vector3.Zero;
            this.MoveSpeed = DefaultForwardSpeed;
            this.RotationSpeed = DefaultRotationSpeed;
            this.Turn(0f, 0f);
        }
        #endregion
        //--------------------------------------------------------//
        #region Methods
        /// <summary>
        /// Moves Camera Forward
        /// </summary>
        public override void MoveForward(float speed)
        {
            Vector3 vector = this.View - this.Position;
            vector.Normalize();
            vector.X *= speed;
            vector.Y *= 0;
            vector.Z *= speed;
            this.Position += vector;
            this.View += vector;
        }
        /// <summary>
        /// Moves Camera Right
        /// </summary>
        public override void MoveRight(float speed)
        {
            Vector3 v = new Vector3(this.Strafe.X * speed, 0, this.Strafe.Z * speed);
            this.Position += v;
            this.View += v;
        }
        /// <summary>
        /// Moves Camera Up
        /// </summary>
        public override void MoveUp(float speed)
        {
            Vector3 v = new Vector3(0, speed, 0);
            this.Position += v;
            this.View += v;
        }
        /// <summary>
        /// Sets Cameras Position
        /// </summary>
        public override void Set(Vector3 pos)
        {
            this.View = (this.View + pos) - this.Position;
            this.Position = pos;
        }
        /// <summary>
        /// Turn Camera
        /// </summary>
        public void Turn(float yaw, float pitch) 
        { 
            this.Pitch += pitch; 
            this.Yaw -= yaw;

            if (this.Pitch > 90f) this.Pitch = 90f;
            if (this.Pitch < -90f) this.Pitch = -90f;

            this.View = new Vector3(
                this.Position.X + MathHelper2.SinDeg(this.Yaw),
                this.Position.Y + MathHelper2.SinDeg(this.Pitch),
                this.Position.Z - MathHelper2.CosDeg(this.Yaw));
        }
        /// <summary>
        /// Set Camera
        /// </summary>
        public void Set(Vector3 pos, float pitch, float yaw)
        {
            this.Set(Position);
            this.Pitch = pitch;
            this.Yaw = yaw;
        }
        /// <summary>
        /// Updates Camera View
        /// </summary>
        public override void Update()
        {
            Vector3 v = Vector3.Cross(this.View - this.Position, this.UpVector);
            v.Normalize();
            this.Strafe = v;
            Matrix4 ModelMatrix = Matrix4.LookAt(this.Position, this.View, this.UpVector);
            // Select The Modelview Matrix
            GL.MatrixMode(MatrixMode.Modelview);
            // Apply Matrix
            GL.LoadMatrix(ref ModelMatrix);
            //Update Frustum
            UpdateFrustum();
        }
        #endregion
        //--------------------------------------------------------//
    }
    //------------------------------------------------------------//
}
